#!/usr/bin/python -S
"""
wsgi_testlib.py
"""

__author__ = 'Andy Chu'

import cStringIO
import urllib


# Convenience functions for tests

def _FakeRequest(method, path_info, query_record=None):
  environ = {'REQUEST_METHOD': method, 'PATH_INFO': path_info}
  if query_record:
    environ['QUERY_STRING'] = urllib.urlencode(query_record)
  return environ


def FakeGET(path_info, query_record=None):
  return _FakeRequest('GET', path_info, query_record=query_record)


def FakeHEAD(path_info, query_record=None):
  return _FakeRequest('HEAD', path_info, query_record=query_record)


def FakePOST(path_info, post_data={}):
  """
  Args:
    post_data: A dictionary, to be sent in the POST body.
  """
  if post_data:
    entity_body = urllib.urlencode(post_data)
  else:
    entity_body = ''
  return {
      'REQUEST_METHOD': 'POST',
      'PATH_INFO': path_info,
      'wsgi.input': cStringIO.StringIO(entity_body),
      }


def FakeMultiPartPOST(path_info, boundary, entity_body): 
  """
  Args:
    entity_body: body of the POST
  """
  return {
      'REQUEST_METHOD': 'POST',
      'PATH_INFO': path_info,
      'wsgi.input': cStringIO.StringIO(entity_body),
      'CONTENT_TYPE': 'multipart/form-data; boundary=%s' % boundary,
      }


class _FakeStartResponse(object):

  def __init__(self, output):
    self._output = output

  def __call__(self, *args, **kwargs):
    self._output.args = args
    self._output.kwargs = kwargs


class WsgiAppResponse(object):
  """
  Given a WSGI application, and an environment dictionary, runs the application
  and makes the output available as the .chunks attribute.
  """

  def __init__(self, app, environ):
    start_response = _FakeStartResponse(self)

    # Also sets self.args, self.kwargs
    chunks = list(app(environ, start_response))
    self.chunks = chunks

  def _Validate(self):
    if not hasattr(self, 'args'):
      raise RuntimeError('start_response was not called')

  def __repr__(self):
    self._Validate()
    tokens = []
    if self.args:
      tokens.append(repr(self.args))
    if self.kwargs:
      tokens.append(repr(self.kwargs))

    tokens.append(repr(self.chunks))
    return ' '.join(tokens)


#
# Not technically related to WSGI, but web apps
#

def _CrossProduct(dictionary):
  if dictionary == {}:
    yield {}  # Base case for recursion
  else:
    base_dict = dict(dictionary)  # copy it
    field, possibilities = base_dict.popitem()  # pops an arbitrary pair
    for p in possibilities:
      for d in _CrossProduct(base_dict):
        d[field] = p
        yield d


def _GenerateQueries(params):
  param_space = {}
  for param in params:
    if param.type == 'string':
      if param.choices:
        param_space[param.name] = param.choices
      else:
        param_space[param.name] = ['', 'a', 'b']  # TODO: add \0, etc.
    elif param.type == 'int':
      param_space[param.name] = [0, 5, 1 << 50]
    elif param.type == 'boolean':
      param_space[param.name] = ['false', 'true']
    elif param.type == 'list':
      param_space[param.name] = [[], ['foo', 'bar']]  # TODO: flesh out
    elif param.type == 'undeclared':
      # TODO: add extra junk for undeclared params
      param_space[param.name] = []  
    else:
      raise AssertionError('Unknown parameter type %r' % param.type)

    # Add None as a placeholder value to indicate that there's no value
    if not param.is_required:
      param_space[param.name].append(None)

  for item in _CrossProduct(param_space):
    # Remove None values
    yield dict((k, v) for k, v in item.iteritems() if v is not None)


def GenerateGetRequests(url, resource):
  get_environ = {'REQUEST_METHOD': 'GET', 'PATH_INFO': url}

  for query_dict in _GenerateQueries(resource.GET_PARAMS):
    #print '*** GET %s?%s' % (url, query_string)
    yield FakeGET(url, query_record=query_dict)


def GeneratePostRequests(url, resource):
  post_environ = {
      'REQUEST_METHOD': 'POST', 'PATH_INFO': url,
      'wsgi.input': cStringIO.StringIO('fake input')}

  for query_dict in _GenerateQueries(resource.POST_PARAMS):
    environ = dict(post_environ)
    query_string = urllib.urlencode(query_dict)
    environ['wsgi.input'] = cStringIO.StringIO(query_string)

    #print '*** POST %s    %s' % (url, query_string)
    yield environ


def GenerateRequests(url, resource):
  """
  Given a url and webapp.Resource instance, read its parameter definition, and
  generate a bunch of WSGI environ dictionaries to request against it.
  """
  for environ in GenerateGetRequests(url, resource):
    yield environ

  for environ in GeneratePostRequests(url, resource):
    yield environ


def SimpleAppTest(app):
  """
  Test that works for apps without regexes in their map.
  """
  for entry in app.url_map:
    # We are abusing orig_pattern and assuming it's ALSO a valid URL, not a
    # regex.
    for environ in GenerateRequests(entry.orig_pattern, entry.resource):
      #print entry.orig_pattern, '!!'
      response = WsgiAppResponse(app, environ)
      args = response.args
